package genotype;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;

import evolutionaryLoop.EvolutionaryParameters;
import evolutionaryProblems.Phenotype;

public abstract class ArrayGenotype1D<T> extends Genotype{

	protected T[] arrayGenotype; 
	
	public ArrayGenotype1D(Class<T> c, int length, EvolutionaryParameters parameters){
		super(parameters); 
		@SuppressWarnings("unchecked")
		T[] arrayGenotype = (T[]) Array.newInstance(c, length); 
		this.arrayGenotype = arrayGenotype;
	}
	
	public void set(T value, int index){
		arrayGenotype[index] = value; 
	}
	public T get(int index){
		return arrayGenotype[index]; 
	}
	public int length(){
		return arrayGenotype.length; 
	}
	public T[] getArrayGenotype(){
		return arrayGenotype; 
	}
	protected void setArrayGeno(T[] arrayGeno){
		this.arrayGenotype = arrayGeno; 
	}
	
	public abstract T mutateValue(T value); 
	
	@Override
	public Genotype mutate() {
		double mutationRate = parameters.getMutationRate(); 
		ArrayGenotype1D<T> clone = clone();
		for (int i = 0; i < clone.length(); i++) {
			if (Math.random() < mutationRate) {
				clone.set(mutateValue(this.get(i)), i);
			}
		}
		return clone;
	}
	
	@Override
	public Collection<Genotype> crossover(Genotype genotype, int crossoverPoints) {
		if (crossoverPoints == 0) {
			System.out.println("CrossoverPoints = 0, changed to 1");
			crossoverPoints = 1; 
		}
		Collection<Genotype> returnCollection = null;
		Genotype p1 = this; 
		Genotype p2 = genotype; 

		for (int i = 0; i < crossoverPoints; i ++){
			returnCollection = p1.crossover(p2);
			boolean flipBit = true; 
			for (Genotype arrayGenotype : returnCollection) {
				if (flipBit){
					flipBit = false; 
					p1 = arrayGenotype;
				} else {
					p2 = arrayGenotype;
				}
			}
		}
		return  returnCollection;
	}
	
	public Collection<Genotype> crossover(Genotype genotype) {
		@SuppressWarnings("unchecked")
		ArrayGenotype1D<T> otherGenotype = (ArrayGenotype1D<T>) genotype; 
		int crossoverPoint = 0 ; 
//		System.out.println(crossoverPoint);
		
//		ArrayGenotype1D<T> new1 = createNewInstance(this.length());
//		ArrayGenotype1D<T> new2 = createNewInstance(otherGenotype.length());

		ArrayGenotype1D<T> new1 = null;
		ArrayGenotype1D<T> new2 = null;
		ArrayGenotype1D<T> shortGeno; 
		ArrayGenotype1D<T> longGeno; 

		
		if (this.length() < otherGenotype.length()){
			new1 = createNewInstance(this.length());
			new2 = createNewInstance(otherGenotype.length());
			shortGeno = this; 
			longGeno = otherGenotype; 
		} else {
			new1 = createNewInstance(otherGenotype.length()); 
			new2 = createNewInstance(this.length());
			shortGeno = otherGenotype; 
			longGeno = this; 
		}
		
		if (this.length() == otherGenotype.length()){
			crossoverPoint = (int) (Math.random()*length()); 
			for (int i = 0; i < length(); i++) {
				if (i < crossoverPoint) {
					new1.set(this.get(i), i); 
					new2.set(otherGenotype.get(i), i); 
				} else {
					new2.set(this.get(i), i); 
					new1.set(otherGenotype.get(i), i); 
				}
			}
		} else {
			crossoverPoint = (int) (Math.random()*shortGeno.length());
			for (int i = 0; i < shortGeno.length(); i++) {
				if (i < crossoverPoint) {
					new1.set(longGeno.get(i), i); 
				} else {
					new1.set(shortGeno.get(i), i); 
				}
			}
			for (int i = 0; i < longGeno.length(); i++) {
				if (i < crossoverPoint) {
					new2.set(shortGeno.get(i), i); 
				} else {
					new2.set(longGeno.get(i), i); 
				}
			}
		}
		Collection<Genotype> returnCollection = new ArrayList<Genotype>(2); 
		returnCollection.add(new1); 
		returnCollection.add(new2); 
		
		return returnCollection;
	}

	@Override
	public abstract Phenotype evolveGenotype();
	
	public abstract ArrayGenotype1D<T> clone();
	public abstract ArrayGenotype1D<T> createNewInstance(int length);
	
}
